import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.security.AlgorithmParameterGenerator;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

public class InstantMessageClient
{

	InetAddress serverIP;
	int serverPort;
	int clientPort;
	byte[] serverPublicKey;
	Cipher serverPublicEncodeCipher;
	Cipher serverPublicDecodeCipher;

	// RSA prviate key cipher created during building connection with server
	Cipher privateEncodeCipher;
	Cipher privateDecodeCipher;

	DatagramSocket serverSocket;
	DatagramSocket clientSocket;
	KeyPair keyPair;

	// flag indicate whether the connection with the server is completed
	boolean isReady = false;

	// self generated random number
	long random1;
	// received from server
	long random2;
	Random rand = new Random(System.currentTimeMillis());

	String user;
	String password;

	BigInteger p512;
	BigInteger g512;
	KeyPair clientKP;
	byte[] cliSecret;

	// shared diffie hillman key with server
	Cipher AESEncodeCipher;
	Cipher AESDecodeCipher;

	// known other clients
	ArrayList<Client> peers = new ArrayList<Client>();

	
	
	public InstantMessageClient(InetAddress ip, int sport, int cport, byte[] key)
	{
		serverIP = ip;
		serverPort = sport;
		clientPort = cport;
		serverPublicKey = key;

	}

	// monitor the request
	public void ListenRequest()
	{
		while (true)
		{
			byte[] receiveData = new byte[4096];
			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
			try
			{
				serverSocket.receive(receivePacket);
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				return;
			}

			if (receivePacket.getLength() < 260)
			{
				System.out.println("Listen Request: the size of receving packet is too small");
				return;
			}

			byte[] cType = new byte[128];
			System.arraycopy(receiveData, 0, cType, 0, 128);
			byte[] msgType;
			try
			{
				msgType = this.privateDecodeCipher.doFinal(cType);
			} catch (Exception e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				continue;
			}

			if (receivePacket.getPort() == serverPort)
			{
				HandleSeverRequest(receivePacket, msgType[0]);
			} else
			{
				HandlePeerRequest(receivePacket, msgType[0]);
			}

		}
	}

	// handle request from server
	public void HandleSeverRequest(DatagramPacket receivePacket, int type)
	{

		byte[] data = null;
		try
		{
			int length = receivePacket.getLength() - 128;
			byte[] rawData = new byte[length];
			System.arraycopy(receivePacket.getData(), 128, rawData, 0, length);
			if (type <= MessageType.loginSessionStartAck)
			{
				data = Utility.blockCipher(rawData, Cipher.DECRYPT_MODE, privateDecodeCipher);
			} else
			{
				if (AESDecodeCipher != null)
				{
					data = AESDecodeCipher.doFinal(rawData, 0, rawData.length);
				} else
				{
					System.out.println("fake server message");
				}
			}
		} catch (IllegalBlockSizeException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		} catch (BadPaddingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}

		if (type == MessageType.loginChallenge)
		{
			handelLoginChallenge(data, receivePacket);
			return;
		}

		if (type == MessageType.loginAck)
		{
			handelLoginAck(data, receivePacket);
			return;
		}

		if (type == MessageType.loginSessionStartAck)
		{
			handelLoginSessionStartAck(data);
			return;
		}

		if (type == MessageType.listResp)
		{
			handelListResponse(data, receivePacket);
			return;
		}

		if (type == MessageType.sendReq)
		{
			handleSendReqMsg(data, receivePacket);
			return;
		}

		if (type == MessageType.sendRspAck)
		{
			this.handleSendRespAck(data, receivePacket);
			return;
		}

		if (type == MessageType.sessionStart)
		{
			this.handleSessionStart(data, receivePacket);
			return;
		}

		if (type == MessageType.sessionStattAck)
		{
			this.handleSessionStartAck(data, receivePacket);
			return;
		}

		if (type == MessageType.broadcastLogoff)
		{
			this.handleBroadcastLogoff(data, receivePacket);
			return;
		}

	}

	// handle request from other clients
	public void HandlePeerRequest(DatagramPacket receivePacket, int type)
	{
		byte[] data = null;
		try
		{
			int length = receivePacket.getLength() - 128;
			byte[] rawData = new byte[length];
			System.arraycopy(receivePacket.getData(), 128, rawData, 0, length);

			Client peer = getPeer(receivePacket.getAddress());
			if (peer == null)
			{
				data = Utility.blockCipher(rawData, Cipher.DECRYPT_MODE, this.privateDecodeCipher);
			} else
			{
				if (peer.status == Client.ClientStatus.Connnected)
				{
					if (peer.AESDecodeCipher != null)
					{
						data = peer.AESDecodeCipher.doFinal(rawData);
					}
				} else
				{
					if (peer.publicDecodeCipher != null)
					{
						data = Utility.blockCipher(rawData, Cipher.DECRYPT_MODE, this.privateDecodeCipher);
					}
				}
			}
		} catch (IllegalBlockSizeException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (type == MessageType.sessionStart)
		{
			this.handleSessionStart(data, receivePacket);
			return;
		}

		if (type == MessageType.sessionStattAck)
		{
			this.handleSessionStartAck(data, receivePacket);
			return;
		}

		if (type == MessageType.msg)
		{
			this.handleTextMsg(data, receivePacket);
			return;
		}
	}

	// search peer in the peers list
	public Client getPeer(InetAddress ip)
	{
		for (int i = 0; i < peers.size(); i++)
		{
			if (peers.get(i).IPAddress != null && peers.get(i).IPAddress.equals(ip))
			{
				return peers.get(i);
			}
		}

		return null;
	}

	// send login message
	public void sendLoginMsg()
	{

		byte[] cType = Utility.createCipheredMsgType(MessageType.login, serverPublicEncodeCipher);
		byte[] data = new byte[4096];
		int index = 1;
		data[0] = 1;

		random1 = rand.nextLong();

		byte[] rand1 = ByteBuffer.allocate(8).putLong(random1).array();
		System.arraycopy(rand1, 0, data, index, 8);
		index += 8;

		byte[] publicKey = keyPair.getPublic().getEncoded();

		byte[] length = ByteBuffer.allocate(4).putInt(publicKey.length).array();

		System.arraycopy(length, 0, data, index, 4);
		index += 4;

		System.arraycopy(publicKey, 0, data, index, publicKey.length);
		index += publicKey.length;

		// to dao, add MAC
		Utility.insertSignature(data, index, this.privateEncodeCipher);

		try
		{

			byte[] cypherdata = Utility.blockCipher(data, Cipher.ENCRYPT_MODE, serverPublicEncodeCipher);

			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);

			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle loginChallenge message
	public void handelLoginChallenge(byte[] d, DatagramPacket receivePacket)
	{

		byte[] data = d;
		int length = data.length;

		InetAddress IPAddress = receivePacket.getAddress();
		Integer port = receivePacket.getPort();

		int index = 1;
		//
		if (length < index + 8)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 8);
		long r = bb.getLong();
		index += 8;

		if (r != random1)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 8);
		random2 = bb.getLong();
		index += 8;

		// todo add hmac

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleLoginChallenge: signature checking failed");
			return;
		}

		sendLoginRespMsg();
	}

	// send loginResp message
	public void sendLoginRespMsg()
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.loginResponse, serverPublicEncodeCipher);
		byte[] data = new byte[4096];

		int index = 1;
		data[0] = 1;

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();

		System.arraycopy(length, 0, data, index, 4);
		index += 4;

		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		try
		{
			Long random = random1 + random2;

			MessageDigest md = MessageDigest.getInstance("SHA-256");
			md.update((user + password).getBytes());
			byte[] passwordDigest = md.digest();
			String text = user + new String(passwordDigest) + random.toString();

			md.update(text.getBytes());
			byte[] digest = md.digest();
			String d = Utility.byteArrayToHexString(passwordDigest);
			byte[] d2 = Utility.hexStringToByteArray(d);

			length = ByteBuffer.allocate(4).putInt(digest.length).array();

			System.arraycopy(length, 0, data, index, 4);
			index += 4;

			System.arraycopy(digest, 0, data, index, digest.length);
			index += digest.length;

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = Utility.blockCipher(data, Cipher.ENCRYPT_MODE, serverPublicEncodeCipher);

			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			// todo hmac
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle login ack message
	public void handelLoginAck(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;

		InetAddress IPAddress = receivePacket.getAddress();
		Integer port = receivePacket.getPort();

		int index = 1;
		//
		if (length < index + 8)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 8);
		long random = bb.getLong();

		if (random != random1 + random2)
		{
			System.out.println("Login Ack: the ramdon num check failed");
			return;
		}
		index += 8;

		bb = ByteBuffer.wrap(data, index, 4);
		int pLength = bb.getInt();
		if (length < index + pLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}

		index += 4;

		byte[] p = new byte[pLength];
		System.arraycopy(data, index, p, 0, pLength);
		p512 = new BigInteger(p);
		index += pLength;

		bb = ByteBuffer.wrap(data, index, 4);
		int gLength = bb.getInt();
		if (length < index + gLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}

		index += 4;

		byte[] g = new byte[gLength];
		System.arraycopy(data, index, g, 0, gLength);
		g512 = new BigInteger(g);
		index += gLength;

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleLoginAck: signature checking failed");
			return;
		}

		sendLoginSessionStartMsg();
	}

	// handle loginsessionstart message
	public void sendLoginSessionStartMsg()
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.loginSessionStart, serverPublicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		byte[] random = ByteBuffer.allocate(8).putLong(random1 + random2).array();
		System.arraycopy(random, 0, data, index, 8);
		index += 8;

		try
		{

			DHParameterSpec cliParamSpec = new DHParameterSpec(p512, g512);
			KeyPairGenerator clientKPG = KeyPairGenerator.getInstance("DH");
			clientKPG.initialize(cliParamSpec);
			clientKP = clientKPG.generateKeyPair();

			byte[] clientpk = clientKP.getPublic().getEncoded();
			byte[] pkLength = ByteBuffer.allocate(4).putInt(clientpk.length).array();
			System.arraycopy(pkLength, 0, data, index, 4);
			index += 4;

			System.arraycopy(clientpk, 0, data, index, clientpk.length);
			index += clientpk.length;

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = Utility.blockCipher(data, Cipher.ENCRYPT_MODE, serverPublicEncodeCipher);

			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			// todo hmac
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle LoginSessionStartAck message
	public void handelLoginSessionStartAck(byte[] d)
	{

		byte[] data = d;
		int length = data.length;

		int index = 1;
		//
		if (length < index + 8)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 8);
		long random = bb.getLong();

		if (random != random1 + random2)
		{
			System.out.println("handelLoginSessionStartAck: the ramdon num check failed");
			return;
		}
		index += 8;

		bb = ByteBuffer.wrap(data, index, 4);
		int modLength = bb.getInt();
		index += 4;

		if (length < index + modLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}

		byte[] mod = new byte[modLength];
		System.arraycopy(data, index, mod, 0, modLength);
		index += modLength;

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleLoginSessionStartAck: signature checking failed");
			return;
		}

		try
		{
			DHPublicKey cliServPub = (DHPublicKey) KeyFactory.getInstance("DH").generatePublic(new X509EncodedKeySpec(mod));

			KeyAgreement cliKA = KeyAgreement.getInstance("DH");
			cliKA.init(clientKP.getPrivate(), new DHParameterSpec(p512, g512));
			cliKA.doPhase(cliServPub, true);
			cliSecret = cliKA.generateSecret();

			AESEncodeCipher = Cipher.getInstance("AES");
			SecretKey k = new SecretKeySpec(cliSecret, 0, 16, "AES");
			AESEncodeCipher.init(Cipher.ENCRYPT_MODE, k);

			AESDecodeCipher = Cipher.getInstance("AES");
			k = new SecretKeySpec(cliSecret, 0, 16, "AES");
			AESDecodeCipher.init(Cipher.DECRYPT_MODE, k);

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		isReady = true;
	}

	// handle sendListReq message
	public void sendListReqMsg()
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.listReq, serverPublicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		random1 = rand.nextLong();
		byte[] rand1 = ByteBuffer.allocate(8).putLong(random1).array();
		System.arraycopy(rand1, 0, data, index, 8);
		index += 8;

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();

		System.arraycopy(length, 0, data, index, 4);
		index += 4;

		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		try
		{

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = AESEncodeCipher.doFinal(data);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);
			int n = 3;
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle ListResponse Message
	public void handelListResponse(byte[] d, DatagramPacket receivePacket)
	{

		byte[] data = d;
		int length = data.length;

		InetAddress IPAddress = receivePacket.getAddress();
		Integer port = receivePacket.getPort();

		int index = 1;
		//
		if (length < index + 32)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 8);
		long r = bb.getLong();
		index += 8;

		if (r != random1)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 8);
		random2 = bb.getLong();
		index += 8;

		bb = ByteBuffer.wrap(data, index, 8);
		long timestamp = bb.getLong();
		Date now = new Date();
		if (now.getTime() - timestamp > 5e6)
		{
			System.out.println("drop the old message");
			return;
		}
		index += 8;

		bb = ByteBuffer.wrap(data, index, 4);
		int count = bb.getInt();
		index += 4;

		for (int i = 0; i < count; i++)
		{
			if (length < index + 4)
			{
				return;
			}

			bb = ByteBuffer.wrap(data, index, 4);
			int usrLength = bb.getInt();
			index += 4;

			if (length < index + usrLength)
			{
				return;
			}

			byte[] user = new byte[usrLength];
			System.arraycopy(data, index, user, 0, usrLength);
			index += usrLength;

			Client peer = new Client(new String(user));
			peer.status = Client.ClientStatus.disConnected;

			System.out.println(new String(user));

		}

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleListResponse: signature checking failed");
			return;
		}

	}

	// send Send message
	public void sendSendMsg(String userB)
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.send, serverPublicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		Client client = getPeerByName(new String(userB));
		if (client == null)
		{
			client = new Client(userB);
			client.status = Client.ClientStatus.WaitingSendRequestAck;
			addPeer(client);
		} else
		{
			client.status = Client.ClientStatus.WaitingSendRequestAck;
		}

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		userLength = userB.length();
		length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		u = userB.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		random1 = rand.nextLong();
		client.random1 = random1;
		byte[] rand1 = ByteBuffer.allocate(8).putLong(random1).array();
		System.arraycopy(rand1, 0, data, index, 8);
		index += 8;

		try
		{
			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = AESEncodeCipher.doFinal(data);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle Send Request message
	public void handleSendReqMsg(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();

		index += 4;

		if (length < index + userALength + 4)
		{
			return;
		}

		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		index += userALength;

		Client client = getPeerByName(new String(userA));

		// if the client already in the client list;
		if (client != null)
		{
			client.status = Client.ClientStatus.WaitingSessionStart;
		} else
		{
			client = new Client(new String(userA));
			client.status = Client.ClientStatus.WaitingSessionStart;
			peers.add(client);
		}

		client.IPAddress = receivePacket.getAddress();

		//
		if (length < index + 8)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 4);
		int userBLength = bb.getInt();

		index += 4;

		if (length < index + userBLength + 12)
		{
			return;
		}

		byte[] userB = new byte[userBLength];
		System.arraycopy(data, index, userB, 0, userBLength);
		index += userBLength;

		bb = ByteBuffer.wrap(data, index, 8);
		client.random1 = bb.getLong();

		index += 8;

		bb = ByteBuffer.wrap(data, index, 4);
		int pubkeyLength = bb.getInt();
		if (length < index + 4)
		{
			return;
		}
		index += 4;

		if (length < index + pubkeyLength)
		{
			return;
		}
		byte[] pubkey = new byte[pubkeyLength];
		System.arraycopy(data, index, pubkey, 0, pubkeyLength);
		index += pubkeyLength;
		client.pubKey = pubkey;
		client.setPublicCipher(pubkey);
		client.random2 = rand.nextLong();

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleSendReqMsg: signature checking failed");
			return;
		}

		sendSendReqAckMsg(client);
	}

	// send SendReqAck message
	public void sendSendReqAckMsg(Client client)
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.sendReqAck, serverPublicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		int userLength = client.name.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = client.name.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		userLength = user.length();
		length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		client.random2 = rand.nextLong();
		byte[] rand2 = ByteBuffer.allocate(8).putLong(client.random2).array();
		System.arraycopy(rand2, 0, data, index, 8);
		index += 8;

		try
		{
			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = AESEncodeCipher.doFinal(data);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverIP, serverPort);
			serverSocket.send(sendPacket);
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle SendResqAck message
	public void handleSendRespAck(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();
		index += 4;
		if (length < index + userALength)
		{
			return;
		}
		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		index += userALength;

		bb = ByteBuffer.wrap(data, index, 4);
		int userBLength = bb.getInt();
		index += 4;
		if (length < index + userALength)
		{
			return;
		}
		byte[] userB = new byte[userBLength];
		System.arraycopy(data, index, userB, 0, userBLength);
		index += userBLength;

		Client client = getPeerByName(new String(userB));

		// if the client already in the client list;
		if (client == null)
		{
			System.out.println("handleSendRespAck: userB doesn't exist");
			return;
		}

		//
		if (length < index + 8)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 8);
		client.random2 = bb.getLong();

		index += 8;

		bb = ByteBuffer.wrap(data, index, 4);
		int ipLength = bb.getInt();
		if (length < index + 4)
		{
			return;
		}
		index += 4;

		byte[] ip = new byte[ipLength];
		if (length < index + ipLength)
		{
			return;
		}
		System.arraycopy(data, index, ip, 0, ipLength);
		index += ipLength;
		try
		{
			client.IPAddress = InetAddress.getByName(new String(ip));
			client.port = clientPort;
		} catch (UnknownHostException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		bb = ByteBuffer.wrap(data, index, 4);
		int pubkeyLength = bb.getInt();
		if (length < index + 4)
		{
			return;
		}
		index += 4;

		byte[] pubkey = new byte[pubkeyLength];
		if (length < index + pubkeyLength)
		{
			return;
		}
		System.arraycopy(data, index, pubkey, 0, pubkeyLength);
		index += pubkeyLength;

		client.pubKey = pubkey;
		client.setPublicCipher(pubkey);

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleSendRespAck: signature checking failed");
			return;
		}

		sendSessionStartMsg(client);
	}

	// send SessionStart message
	public void sendSessionStartMsg(Client client)
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.sessionStart, client.publicEncodeCipher);
		byte[] data = new byte[2048];
		data[0] = MessageType.sessionStart;
		int index = 1;
		data[0] = 1;

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		userLength = client.name.length();
		length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		u = client.name.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		byte[] rand1 = ByteBuffer.allocate(8).putLong(client.random1).array();
		System.arraycopy(rand1, 0, data, index, 8);
		index += 8;

		byte[] rand2 = ByteBuffer.allocate(8).putLong(client.random2).array();
		System.arraycopy(rand2, 0, data, index, 8);
		index += 8;

		try
		{

			AlgorithmParameterGenerator servAPG = AlgorithmParameterGenerator.getInstance("DH");
			servAPG.init(512);
			DHParameterSpec servParamSpec;
			servParamSpec = servAPG.generateParameters().getParameterSpec(DHParameterSpec.class);
			BigInteger p512 = servParamSpec.getP();
			BigInteger g512 = servParamSpec.getG();

			byte[] p = p512.toByteArray();
			byte[] pLength = ByteBuffer.allocate(4).putInt(p.length).array();
			System.arraycopy(pLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(p, 0, data, index, p.length);
			index += p.length;

			// set g
			byte[] g = g512.toByteArray();
			byte[] gLength = ByteBuffer.allocate(4).putInt(g.length).array();
			System.arraycopy(gLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(g, 0, data, index, g.length);
			index += g.length;

			client.setDHKeyPair(p512, g512);

			byte[] mod = client.DHKeyPair.getPublic().getEncoded();
			byte[] modLength = ByteBuffer.allocate(4).putInt(mod.length).array();
			System.arraycopy(modLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(mod, 0, data, index, mod.length);
			index += mod.length;

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = Utility.blockCipher(data, Cipher.ENCRYPT_MODE, client.publicEncodeCipher);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, client.IPAddress, client.port);
			serverSocket.send(sendPacket);

			client.status = Client.ClientStatus.WaitingSessionStartAck;
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle SessionStart Message
	public void handleSessionStart(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();
		index += 4;
		if (length < index + userALength + 4)
		{
			return;
		}
		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		index += userALength;

		bb = ByteBuffer.wrap(data, index, 4);
		int userBLength = bb.getInt();
		index += 4;
		if (length < index + userBLength)
		{
			return;
		}
		byte[] userB = new byte[userBLength];
		System.arraycopy(data, index, userB, 0, userBLength);
		index += userBLength;

		if (!user.equals(new String(userB)))
		{
			System.out.println("I am not the recipient of the message");
			return;
		}

		Client client = getPeerByName(new String(userA));
		client.IPAddress = receivePacket.getAddress();

		if (client == null)
		{
			return;
		}

		if (client.status != Client.ClientStatus.WaitingSessionStart)
		{
			System.out.println("handleSessionStart: the status of sender is  waitingsessionstart");
			return;
		}

		if (length < index + 32)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 8);
		long rand1 = bb.getLong();
		if (rand1 != client.random1)
		{
			return;
		}

		index += 8;

		bb = ByteBuffer.wrap(data, index, 8);
		long rand2 = bb.getLong();
		if (rand2 != client.random2)
		{
			return;
		}

		index += 8;
		if (length < index + 4)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		bb = ByteBuffer.wrap(data, index, 4);
		int pLength = bb.getInt();
		index += 4;
		if (length < index + pLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		byte[] p = new byte[pLength];
		System.arraycopy(data, index, p, 0, pLength);
		BigInteger p1 = new BigInteger(p);
		index += pLength;

		if (length < index + 4)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		bb = ByteBuffer.wrap(data, index, 4);
		int gLength = bb.getInt();
		index += 4;
		if (length < index + gLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		byte[] g = new byte[gLength];
		System.arraycopy(data, index, g, 0, gLength);
		BigInteger g1 = new BigInteger(g);
		index += gLength;

		bb = ByteBuffer.wrap(data, index, 4);
		int modLength = bb.getInt();
		index += 4;
		if (length < index + modLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}

		byte[] mod = new byte[modLength];
		System.arraycopy(data, index, mod, 0, modLength);
		index += modLength;

		client.setDHKeyPair(p1, g1);

		try
		{
			DHPublicKey cliServPub = (DHPublicKey) KeyFactory.getInstance("DH").generatePublic(new X509EncodedKeySpec(mod));

			KeyAgreement serverKA = KeyAgreement.getInstance("DH");
			serverKA.init(client.DHKeyPair.getPrivate(), new DHParameterSpec(p1, g1));
			serverKA.doPhase(cliServPub, true);
			client.cliSerKey = serverKA.generateSecret();
			client.initAESCipher(client.cliSerKey);

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (!Utility.checkSignature(data, index, client.publicDecodeCipher))
		{
			System.out.println("handleSessionStart: signature checking failed");
			return;
		}

		sendSessionStartMsgAck(client, p1.toByteArray(), g1.toByteArray());
	}

	// handle SessionStartAck message
	public void sendSessionStartMsgAck(Client client, byte[] p, byte[] g)
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.sessionStattAck, client.publicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		int userLength = client.name.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = client.name.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		userLength = user.length();
		length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		byte[] rand1 = ByteBuffer.allocate(8).putLong(client.random1).array();
		System.arraycopy(rand1, 0, data, index, 8);
		index += 8;

		byte[] rand2 = ByteBuffer.allocate(8).putLong(client.random2).array();
		System.arraycopy(rand2, 0, data, index, 8);
		index += 8;

		try
		{

			byte[] pLength = ByteBuffer.allocate(4).putInt(p.length).array();
			System.arraycopy(pLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(p, 0, data, index, p.length);
			index += p.length;

			// set g

			byte[] gLength = ByteBuffer.allocate(4).putInt(g.length).array();
			System.arraycopy(gLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(g, 0, data, index, g.length);
			index += g.length;

			byte[] mod = client.DHKeyPair.getPublic().getEncoded();
			byte[] modLength = ByteBuffer.allocate(4).putInt(mod.length).array();
			System.arraycopy(modLength, 0, data, index, 4);
			index += 4;
			System.arraycopy(mod, 0, data, index, mod.length);
			index += mod.length;

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = Utility.blockCipher(data, Cipher.ENCRYPT_MODE, client.publicEncodeCipher);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, client.IPAddress, clientPort);
			serverSocket.send(sendPacket);

			client.status = Client.ClientStatus.Connnected;
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle sessionStartAck message
	public void handleSessionStartAck(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();
		index += 4;
		if (length < index + userALength)
		{
			return;
		}
		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		if (!user.equals(new String(userA)))
		{
			System.out.println("I am not the recipient of the message");
		}
		index += userALength;

		bb = ByteBuffer.wrap(data, index, 4);
		int userBLength = bb.getInt();
		index += 4;
		if (length < index + userBLength)
		{
			return;
		}
		byte[] userB = new byte[userBLength];
		System.arraycopy(data, index, userB, 0, userBLength);
		index += userBLength;

		Client client = getPeerByName(new String(userB));

		if (client == null)
		{
			return;
		}

		if (client.status != Client.ClientStatus.WaitingSessionStartAck)
		{
			return;
		}

		//
		if (length < index + 8)
		{
			return;
		}

		bb = ByteBuffer.wrap(data, index, 8);
		long rand1 = bb.getLong();
		if (rand1 != client.random1)
		{
			return;
		}

		index += 8;

		bb = ByteBuffer.wrap(data, index, 8);
		long rand2 = bb.getLong();
		if (rand2 != client.random2)
		{
			return;
		}

		index += 8;

		if (length < index + 4)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		bb = ByteBuffer.wrap(data, index, 4);
		int pLength = bb.getInt();
		index += 4;
		if (length < index + pLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		byte[] p = new byte[pLength];
		System.arraycopy(data, index, p, 0, pLength);
		BigInteger p1 = new BigInteger(p);
		index += pLength;

		if (length < index + 4)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		bb = ByteBuffer.wrap(data, index, 4);
		int gLength = bb.getInt();
		index += 4;
		if (length < index + gLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}
		byte[] g = new byte[gLength];
		System.arraycopy(data, index, g, 0, gLength);
		BigInteger g1 = new BigInteger(g);
		index += gLength;

		bb = ByteBuffer.wrap(data, index, 4);
		int modLength = bb.getInt();
		if (length < index + modLength)
		{
			System.out.println("exceed the buffer size");
			return;
		}

		index += 4;

		byte[] mod = new byte[modLength];
		System.arraycopy(data, index, mod, 0, modLength);
		index += modLength;

		if (!Utility.checkSignature(data, index, client.publicDecodeCipher))
		{
			System.out.println("handleSessionStartAck: signature checking failed");
			return;
		}

		try
		{
			DHPublicKey cliServPub = (DHPublicKey) KeyFactory.getInstance("DH").generatePublic(new X509EncodedKeySpec(mod));

			KeyAgreement serverKA = KeyAgreement.getInstance("DH");
			serverKA.init(client.DHKeyPair.getPrivate(), new DHParameterSpec(p1, g1));
			serverKA.doPhase(cliServPub, true);
			client.cliSerKey = serverKA.generateSecret();
			client.initAESCipher(client.cliSerKey);
			client.status = Client.ClientStatus.Connnected;
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// send text to anther user
	public void sendText(String receiver, String msg)
	{
		Client peer = this.getPeerByName(receiver);
		if (peer != null && peer.AESEncodeCipher != null && peer.IPAddress != null)
		{
			this.sendTextMsg(peer, msg);
		} else
		{
			this.sendSendMsg(receiver);
			try
			{
				Thread.sleep(1400);
			} catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			peer = this.getPeerByName(receiver);
			if (peer != null && peer.AESEncodeCipher != null && peer.IPAddress != null)
			{
				this.sendTextMsg(peer, msg);
			} else
			{
				System.out.println("fail to initilize connection with " + receiver);
			}

		}
	}

	public void sendTextMsg(Client client, String msg)
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.msg, client.publicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		int msgLength = msg.length();
		if (index + msgLength > 1600)
		{
			msgLength = 1600 - index;
		}

		length = ByteBuffer.allocate(4).putInt(msgLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;

		System.arraycopy(msg.getBytes(), 0, data, index, msgLength);
		index += msgLength;

		try
		{

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = client.AESEncodeCipher.doFinal(data);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, client.IPAddress, clientPort);
			serverSocket.send(sendPacket);

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle Text Message
	public void handleTextMsg(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();
		index += 4;
		if (length < index + userALength)
		{
			return;
		}
		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		index += userALength;

		bb = ByteBuffer.wrap(data, index, 4);
		int textLength = bb.getInt();
		index += 4;
		if (length < index + textLength)
		{
			return;
		}
		byte[] text = new byte[textLength];
		System.arraycopy(data, index, text, 0, textLength);
		index += textLength;

		Client client = getPeerByName(new String(userA));

		if (client == null)
		{
			System.out.println("handleTextMsg: ClientA is null");
			return;
		}

		if (!Utility.checkSignature(data, index, client.publicDecodeCipher))
		{
			System.out.println("handleTextMsg: signature checking failed");
			return;
		}

		System.out.println(new String(userA) + ": " + new String(text));
	}

	// send logoff message
	public void sendLogOff()
	{
		byte[] cType = Utility.createCipheredMsgType(MessageType.logoff, this.serverPublicEncodeCipher);
		byte[] data = new byte[2048];

		int index = 1;
		data[0] = 1;

		int userLength = user.length();
		byte[] length = ByteBuffer.allocate(4).putInt(userLength).array();
		System.arraycopy(length, 0, data, index, 4);
		index += 4;
		byte[] u = user.getBytes();
		System.arraycopy(u, 0, data, index, userLength);
		index += userLength;

		try
		{

			Utility.insertSignature(data, index, this.privateEncodeCipher);

			byte[] cypherdata = this.AESEncodeCipher.doFinal(data);
			byte[] sendData = Utility.combineTwoByteArray(cType, cypherdata);
			DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, this.serverIP, this.serverPort);
			serverSocket.send(sendPacket);

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// handle broadcastlogoff message
	public void handleBroadcastLogoff(byte[] d, DatagramPacket receivePacket)
	{
		byte[] data = d;
		int length = data.length;
		int index = 1;

		if (length < index + 4)
		{
			return;
		}

		ByteBuffer bb = ByteBuffer.wrap(data, index, 4);
		int userALength = bb.getInt();
		index += 4;
		if (length < index + userALength)
		{
			return;
		}
		byte[] userA = new byte[userALength];
		System.arraycopy(data, index, userA, 0, userALength);
		index += userALength;

		if (!Utility.checkSignature(data, index, this.serverPublicDecodeCipher))
		{
			System.out.println("handleBroadcastLogoff: signature checking failed");
			return;
		}

		if (new String(userA).equals(this.user))
		{
			System.out.println("you are login somewhere else");
		}

		this.removePeerByName(new String(userA));
		System.out.println(new String(userA) + " exits the system");
	}

	public void init()
	{

		try
		{
			serverSocket = new DatagramSocket(clientPort);
			clientSocket = new DatagramSocket();

			KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
			keyGen.initialize(1024);
			keyPair = keyGen.generateKeyPair();

			X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.decode(serverPublicKey));
			KeyFactory kf = KeyFactory.getInstance("RSA");
			PublicKey publick = kf.generatePublic(publicKeySpec);
			serverPublicEncodeCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
			serverPublicEncodeCipher.init(Cipher.ENCRYPT_MODE, publick);

			serverPublicDecodeCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
			serverPublicDecodeCipher.init(Cipher.DECRYPT_MODE, publick);

			privateEncodeCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
			privateEncodeCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());

			privateDecodeCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
			privateDecodeCipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());

			Thread t = new Thread(new Runnable()
			{

				public void run()
				{

					ListenRequest();

				}
			});

			t.start();

		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String args[]) throws Exception
	{

		File privateKeyFile = new File("serverpublic.key");
		DataInputStream in = new DataInputStream(new FileInputStream(privateKeyFile));
		byte[] publicKey = new byte[(int) privateKeyFile.length()];
		in.readFully(publicKey);

		File configFile = new File("clientConfig.txt");
		in = new DataInputStream(new FileInputStream(configFile));
		String line = in.readLine();

		String ip;
		if (line.contains("serverIP"))
		{
			ip = line.split(":")[1];
		} else
		{
			System.out.println("need set the server IP");
			return;
		}

		line = in.readLine();
		int serverPort;
		if (line.contains("serverPort"))
		{
			serverPort = Integer.valueOf(line.split(":")[1]);
		} else
		{
			System.out.println("need set server port");
			return;
		}

		line = in.readLine();
		int clientPort;
		if (line.contains("clientPort"))
		{
			clientPort = Integer.valueOf(line.split(":")[1]);
		} else
		{
			System.out.println("need set client port");
			return;
		}

		in.close();

		Security.addProvider(new BouncyCastleProvider());

		InetAddress serverIP = InetAddress.getByName(ip);

		InstantMessageClient client = new InstantMessageClient(serverIP, serverPort, clientPort, publicKey);
		System.out.println("initializing");
		client.init();

		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		String s;
		System.out.println("please enter usrname");

		while ((s = stdin.readLine()) != null)
		{
			String username = s;
			System.out.println("please enter password");
			s = stdin.readLine();
			String password = s;

			client.password = password;
			client.user = username;
			client.sendLoginMsg();
			Thread.sleep(1500);
			for (int i = 0; i < 2; i++)
			{

				if (client.isReady)
					break;
				else
					client.sendLoginMsg();
				Thread.sleep(1500);
			}

			if (client.isReady)
				break;
			else
			{
				System.out.println("password is wrong or server is down");
				System.out.println("please enter usrname");
			}
		}

		System.out.println("login success, please enter the command");
		while ((s = stdin.readLine()) != null)
		{
			if (s.equals("logout"))
			{
				client.sendLogOff();
				break;
			} else
			{
				if (s.equals("list"))
				{
					client.sendListReqMsg();
				} else
				{
					if (s.contains("send"))
					{
						String s2 = s.replaceAll(" +", " ");
						String[] tokens = s2.split(" ");
						if (tokens.length > 2)
						{
							String peerName = tokens[1];
							if (peerName.equals(client.user))
							{
								System.out.println("sorry, you don't need to send msg to yourself");
							} else
							{
								client.sendText(tokens[1], tokens[2]);
							}
						}
					} else
					{
						System.out.println("wrong command, only support \"logout, list, send\"");
					}
				}
			}
		}
	}

	private Client getPeerByName(String name)
	{

		for (int i = 0; i < peers.size(); i++)
		{
			if (peers.get(i).name.equals(name))
			{
				return peers.get(i);
			}
		}

		return null;
	}

	private void removePeerByName(String name)
	{

		for (int i = 0; i < peers.size(); i++)
		{
			if (peers.get(i).name.equals(name))
			{
				peers.remove(i);
			}
		}
	}

	private void addPeer(Client client)
	{
		if (getPeerByName(client.name) == null)
		{
			peers.add(client);
		}
	}
}
